﻿using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Shipeng.Util;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using YNTK.Common.Const;
using YNTK.Common.Enum;
using YNTK.Common.Filter;
using YNTK.Common.Util;
using YNTK.Data.MongoDB.Repositorie;
using YNTK.Dependency;
using YNTK.DynamicApiController;
using YNTK.FriendlyException;
using YNTK.LinqBuilder;
using YNTK.Mapster;
using YNTK.System.Entitys.Dto.Common;
using YNTK.System.Entitys.Dto.Resources;
using YNTK.System.Entitys.Permission;
using YNTK.System.Entitys.Resources;
using YNTK.System.Interfaces.Resources;

namespace YNTK.System.Service.Resources
{
    /// <summary>
    /// 字典服务的实现
    /// 版 本：V1.0.0
    /// 版 权：云南通科科技有限公司（https://www.yntkkj.com）
    /// 作 者：李仕鹏
    /// 日 期：2022-10-02 
    /// </summary>
    [ApiDescriptionSettings(Tag = "字典管理（新）", Name = "Dictionary", Order = 797)]
    [Route("api/[controller]")]
    public class DictionaryService: IDictionaryService, IDynamicApiController, ITransient
    {
        private readonly ISqlSugarRepository<DictionaryEntity> _dictionaryRepository;//字典仓储
        private readonly IMongoDBRepository<DictionaryEntity> _dictionaryMongoDbRepository;//字典mongodb仓储
        private readonly ISqlSugarRepository<UserEntity> _tkUserRepository;//用户仓储

        private readonly HttpContext _httpContext;
        private readonly string _collection = "base_dictionarys";//mongodb中字典数据集名称

        /// <summary>
        /// 构造方法依赖注入
        /// </summary>
        /// <param name="dictionaryRepository"></param>
        /// <param name="dictionaryMongoDbRepository">字典mongodb仓储</param>
        /// <param name="tkUserRepository">用户仓储</param>
        public DictionaryService(ISqlSugarRepository<DictionaryEntity> dictionaryRepository,
            IMongoDBRepository<DictionaryEntity> dictionaryMongoDbRepository,
            ISqlSugarRepository<UserEntity> tkUserRepository)
        {
            _dictionaryRepository = dictionaryRepository;
            _dictionaryMongoDbRepository = dictionaryMongoDbRepository;
            _tkUserRepository = tkUserRepository;
            _httpContext = App.HttpContext;
        }

        /// <summary>
        /// 获取字典项数据
        /// </summary>
        /// <returns></returns>
        [HttpGet("get/type")]
        public TkResult<List<EnumListDto>> GetDictionaryTypeList()
        {
            Dictionary<int, string> dic = EnumHelper.EnumToDic<DictionaryType>();
            var result = new List<EnumListDto>();
            foreach (KeyValuePair<int, string> kv in dic)
            {
                result.Add(new EnumListDto {Id=kv.Value,Value=kv.Key,Description=kv.Value});
            }
            return TkResult<List<EnumListDto>>.Success(result);
        }

        /// <summary>
        /// 根据字典项获取字典值树结构数据
        /// </summary>
        /// <param name="type">字典项</param>
        /// <param name="keyword">模糊搜索关键字,字典项、编号</param>
        /// <returns></returns>
        [HttpGet("gets/{type}")]
        public async Task<TkResult<List<DictionaryDataTreeDto>>> Gets([Required] int type,string keyword)
        {
            try
            {
                var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
                string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
                //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
                //var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;

                Expression<Func<DictionaryEntity, bool>> where = m =>
                m.OrganizeId == organizeId && m.Type==type && m.DeleteMark == null;
                if (!string.IsNullOrEmpty(keyword))
                    where = where.And(m=>m.Code.Contains(keyword) || m.Name.Contains(keyword));

                //从mongdodb中获取
                var data = await _dictionaryMongoDbRepository.GetListAsync(_collection,where);
                if (!data.Any())
                {
                    //再从sqlserver中获取
                    data = await _dictionaryRepository.Entities.Where(where).ToListAsync();
                }
                data = data?.OrderByDescending(r => r.CreatorTime)?.OrderBy(r => r.SortCode)?.ToList();
                var treeList = data.Adapt<List<DictionaryDataTreeDto>>();
                if (treeList.Any())
                {
                    var userIds = treeList.Select(r => r.creatorUserId).Distinct().ToList();
                    var users = await _tkUserRepository.Entities.Where(u=> userIds.Contains(u.Id)).ToListAsync();
                    treeList.ForEach(item =>
                    {
                        if (!string.IsNullOrWhiteSpace(item.creatorUserId))
                            item.creatorUserName = users?.FirstOrDefault(r => r.Id == item.creatorUserId)?.RealName;
                    });
                }
                treeList = treeList.ToTree();
                return TkResult<List<DictionaryDataTreeDto>>.Success(treeList);
            }
            catch (Exception)
            {
                throw YNTKException.Oh("根据字典分类获取字典树结构数据发生异常");
            }
        }

        /// <summary>
        /// 根据字典项获取启用字典值树结构数据(使用的字典值的地方使用)
        /// </summary>
        /// <param name="type">字典项</param>
        /// <returns></returns>
        [HttpGet("getTree/{type}")]
        public async Task<TkResult<List<DictionaryDataTreeDto>>> GetTree([Required] int type)
        {
            try
            {
                var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
                string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;

                Expression<Func<DictionaryEntity, bool>> where = m =>
               m.OrganizeId == organizeId && m.Type == type && m.EnabledMark==1 && m.DeleteMark == null;

                //从mongdodb中获取
                var data = await _dictionaryMongoDbRepository.GetListAsync(_collection, where);
                if (!data.Any())
                {
                    //再从sqlserver中获取
                    data = await _dictionaryRepository.Entities.Where(where).ToListAsync();
                }
                data = data?.OrderByDescending(r => r.CreatorTime)?.OrderBy(r => r.SortCode)?.ToList();
                var treeList = data.Adapt<List<DictionaryDataTreeDto>>();
                if (treeList.Any())
                {
                    var userIds = treeList.Select(r => r.creatorUserId).Distinct().ToList();
                    var users = await _tkUserRepository.Entities.Where(u => userIds.Contains(u.Id)).ToListAsync();
                    treeList.ForEach(item =>
                    {
                        if (!string.IsNullOrWhiteSpace(item.creatorUserId))
                            item.creatorUserName = users?.FirstOrDefault(r => r.Id == item.creatorUserId)?.RealName;
                    });
                }
                treeList = treeList.ToTree();
                return TkResult<List<DictionaryDataTreeDto>>.Success(treeList);
            }
            catch (Exception)
            {
                throw YNTKException.Oh("根据字典项获取启用字典值树结构数据发生异常");
            }
        }

        /// <summary>
        /// 添加字典值
        /// </summary>
        /// <param name="input">添加字典值请求模型</param>
        /// <returns></returns>
        [HttpPost("create")]
        public async Task Create([FromBody] AddDictionaryDataRequestModel input)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
            //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
            //var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;

            if (!Enum.IsDefined(input.type)) throw YNTKException.Oh(ErrorCode.D3000);
            if((await GetDictionaryInfoAsync(d=>d.OrganizeId==organizeId && d.Type==input.type.GetHashCode() && 
            d.Code==input.code && d.DeleteMark==null))!=null) throw YNTKException.Oh(ErrorCode.D3009);
            if ((await GetDictionaryInfoAsync(d => d.OrganizeId == organizeId && d.Type == input.type.GetHashCode() &&
            d.Name == input.name && d.DeleteMark == null)) != null) throw YNTKException.Oh(ErrorCode.D3010);
            var entity = input.Adapt<DictionaryEntity>();
            entity.OrganizeId = organizeId;//所属组织id
            entity.Type = input.type.GetHashCode();//字典分类

            //保存字典值到sqlserver
            var isOk = await _dictionaryRepository.Context.Insertable(entity)
                .CallEntityMethod(m => m.Creator()).ExecuteReturnEntityAsync();
            _ = isOk ?? throw YNTKException.Oh(ErrorCode.D3011);

            //保存字典值到mongodb
            entity = isOk;
            var result = await _dictionaryMongoDbRepository.AddAsync(entity, _collection);
            if (!result) throw YNTKException.Oh(ErrorCode.D3015);
        }

        /// <summary>
        /// 修改字典值
        /// </summary>
        /// <param name="input">修改字典值请求模型</param>
        /// <returns></returns>
        [HttpPut("update")]
        public async Task Update([FromBody] UpdateDictionaryDataRequestModel input)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            string userId = user.Value;
            string organizeId = user.Subject.FindFirst(ClaimConst.ORGANIZEID).Value;
    
            if (!Enum.IsDefined(input.type)) throw YNTKException.Oh(ErrorCode.D3000);
            var entity = await GetDictionaryInfoAsync(d => d.Id == input.id && d.DeleteMark == null);
            _ = entity ?? throw YNTKException.Oh(ErrorCode.D3004);
            if (entity.CreatorUserId != userId) throw YNTKException.Oh(ErrorCode.D1016);

            if ((await GetDictionaryInfoAsync(d => d.OrganizeId == organizeId && d.Type == input.type.GetHashCode() &&
           d.Code == input.code && d.Id!=input.id && d.DeleteMark == null)) != null) 
                throw YNTKException.Oh(ErrorCode.D3009);
            if ((await GetDictionaryInfoAsync(d => d.OrganizeId == organizeId && d.Type == input.type.GetHashCode() &&
            d.Name == input.name && d.Id!=input.id && d.DeleteMark == null)) != null) 
                throw YNTKException.Oh(ErrorCode.D3010);

            //var entity = input.Adapt<DictionaryEntity>();
            entity = input.Mapper(entity);
            entity.OrganizeId = organizeId;//所属组织id
            entity.Type = input.type.GetHashCode();//字典分类
            entity.LastModifyTime = DateTime.Now;
            entity.LastModifyUserId = userId;
            //保存字典值到sqlserver
            var isOK = await _dictionaryRepository.Context.Updateable(entity)
            .IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync();
            if (!(isOK > 0)) throw YNTKException.Oh(ErrorCode.D3012);

            //保存字典值到mongodb
            var update = await _dictionaryMongoDbRepository.UpdateAsync(entity, _collection, m => m.Id == entity.Id);
            if (!(update.ModifiedCount > 0)) throw YNTKException.Oh(ErrorCode.D3016);
        }

        /// <summary>
        /// 删除字典项
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        [HttpDelete("delete/{id}")]
        public async Task Delete([Required] string id)
        {
            var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
            string userId = user.Value;

            //var entity = await _dictionaryRepository.SingleAsync(o => o.Id == id && o.DeleteMark == null);
            var entity = await GetDictionaryInfoAsync(d => d.Id ==id && d.DeleteMark == null);
            _ = entity ?? throw YNTKException.Oh(ErrorCode.D3004);
            if (entity.CreatorUserId != userId) throw YNTKException.Oh(ErrorCode.D1016);

            //先从sqlserver中删除
            var isOk = await _dictionaryRepository.Context.Deleteable(entity).ExecuteCommandAsync();
            if (!(isOk > 0)) throw YNTKException.Oh(ErrorCode.D3013);

            //再从mongodb中删除
            var delete = await _dictionaryMongoDbRepository.DeleteAsync(_collection, m => m.Id == id);
            if (!(delete > 0)) throw YNTKException.Oh(ErrorCode.D3017);
        }

        /// <summary>
        /// 更新字典值状态(启用/禁用)
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        [HttpPut("state/{id}")]
        public async Task UpdateState([Required] string id)
        {
            try
            {
                var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
                string userId = user.Value;

                var entity = await GetDictionaryInfoAsync(d => d.Id == id && d.DeleteMark == null);
                _ = entity ?? throw YNTKException.Oh(ErrorCode.D3004);
                if (entity.CreatorUserId != userId) throw YNTKException.Oh(ErrorCode.D1016);

                //更新sqlserver中字典值状态
                var isOk = await _dictionaryRepository.Context.Updateable<DictionaryEntity>().SetColumns(it => new DictionaryEntity()
                {
                    EnabledMark = SqlFunc.IIF(it.EnabledMark == 1, 0, 1),
                    LastModifyUserId = userId,
                    LastModifyTime = SqlFunc.GetDate()
                }).Where(it => it.Id == id).ExecuteCommandAsync();
                if (!(isOk > 0))
                    throw YNTKException.Oh(ErrorCode.D3014);

                //更新mongodb中字典值状态
                entity.EnabledMark = entity.EnabledMark == 1 ? 0 : 1;
                entity.LastModifyTime = DateTime.Now;
                entity.LastModifyUserId = userId;
                var update = await _dictionaryMongoDbRepository.UpdateAsync(entity, _collection, m => m.Id == entity.Id);
                if (!(update.ModifiedCount > 0)) throw YNTKException.Oh(ErrorCode.D3018);
            }
            catch (Exception)
            {
                throw YNTKException.Oh("更新组织状态发生异常");
            }
        }

        /// <summary>
        /// 同步字典数据(开发人员同步专用)
        /// </summary>
        /// <param name="type">0.sqlserver同步mongodb 1.mongodb同步sqlserver</param>
        /// <param name="organizeId">要同步的组织id</param>
        /// <param name="startTime">要同步的数据范围开始时间</param>
        /// <param name="endTime">要同步的数据范围截止时间</param>
        /// <returns></returns>
        [HttpPut("synchronous/{type}")]
        public async Task Synchronous([Required] int type, string organizeId, DateTime? startTime, DateTime? endTime)
        {
            try
            {
                var user = _httpContext.User.FindFirst(ClaimConst.CLAINM_USERID);
                string userId = user.Value;
                //账号类型【0.平台运营人员 1.超级管理员 2.组织管理员 3.组织用户 4.第三方登录用户 5.游客用户】
                var authenticationType = user.Subject.FindFirst(ClaimConst.ACCOUNTTYPE).Value;
                if (authenticationType != "1" || (type!=0 && type!=1)) throw YNTKException.Oh(ErrorCode.D1016);
                _dictionaryRepository.Ado.BeginTran();
                List<DictionaryEntity> data = null;
                Expression<Func<DictionaryEntity, bool>> where = r => r.DeleteMark == null;
                if (!string.IsNullOrWhiteSpace(organizeId)) where = where.And(r=>r.OrganizeId== organizeId);
                if (startTime != null) where = where.And(r=>r.CreatorTime>=startTime);
                if (endTime != null) where = where.And(r => r.CreatorTime <= endTime);
                if (type == 0)
                {
                    data = await _dictionaryRepository.Entities.Where(where).ToListAsync();
                    if (data.Any())
                    {
                        //删除mongodb原数据
                        await _dictionaryMongoDbRepository.DeleteAsync(_collection, where);
                        //保存到mongodb
                        var succeed = await _dictionaryMongoDbRepository.AddListAsync(data, _collection);
                        if (!succeed) throw YNTKException.Oh("字典数据sqlserver同步mongodb失败");
                    }
                }
                if (type == 1)
                {
                    data = await _dictionaryMongoDbRepository.GetListAsync(_collection, where);
                    if (data.Any())
                    {
                        //删除sqlserver原数据
                        await _dictionaryRepository.DeleteAsync(where);
                        //保存到sqlserver                 
                        var isOk = await _dictionaryRepository.Context.Insertable(data).ExecuteCommandAsync();
                        if (!(isOk > 0)) throw YNTKException.Oh("字典数据mongodb同步sqlserver失败");
                    }
                }
                _dictionaryRepository.Ado.CommitTran();
            }
            catch (Exception ex)
            {
                _dictionaryRepository.Ado.RollbackTran();
                if (ex.Message.Contains("[")) throw YNTKException.Oh($"{ex.Message}");
                throw YNTKException.Oh($"同步字典数据发生异常：{ex.Message}");
            }
        }

        /// <summary>
        /// 根据条件获取字典值信息
        /// </summary>
        /// <param name="where">条件</param>
        /// <returns></returns>
        private async Task<DictionaryEntity> GetDictionaryInfoAsync(Expression<Func<DictionaryEntity, bool>> where)
        {
            //从从mongodb中获取
            var entity = await _dictionaryMongoDbRepository.GetAsync(_collection, where);
            //再从sqlserver中获取
            if (entity == null) entity = await _dictionaryRepository.FirstOrDefaultAsync(where);
            return entity;
        }


        /// <summary>
        /// 根据id获取字典值
        /// </summary>
        /// <param name="id">id</param>
        /// <returns></returns>
        [NonAction]
        public async Task<DictionaryEntity> GetDictionaryInfoAsync(string id)
        {
            //先从mongodb中获取字典值
            var entity = await _dictionaryMongoDbRepository.GetAsync(_collection,m=>m.Id==id);
            //再从sqlserver中获取字典值
            if (entity == null) entity = await _dictionaryRepository.FirstOrDefaultAsync(r=>r.Id==id);
            return entity;
        }

        /// <summary>
        /// 根据条件获取字典数据
        /// </summary>
        /// <param name="where">条件</param>
        /// <returns></returns>
        [NonAction]
        public async Task<List<DictionaryEntity>> GetDictionaryListAsync(Expression<Func<DictionaryEntity, bool>> where)
        {
            //先从mongodb中获取
            var result = await _dictionaryMongoDbRepository.GetListAsync(_collection, where);
            if (!result.Any())
            {
                //再从sqlserver中获取
                result = await _dictionaryRepository.Entities.Where(where).ToListAsync();
            }
            return result;
        }

        /// <summary>
        /// 根据字典值id集合获取字典值数据
        /// </summary>
        /// <param name="ids">字典值id集合</param>
        /// <returns></returns>
        [NonAction]
        public async Task<List<DictionaryEntity>> GetDictionaryListByIds(List<string> ids)
        {
            var result = new List<DictionaryEntity>();
            if (ids == null || ids.Count <= 0) return result;
            //先从mongodb中获取
            result = await _dictionaryMongoDbRepository.GetListAsync(_collection,m=>ids.Contains(m.Id) && m.DeleteMark==null);
            if (!result.Any())
            {
                //再从sqlserver中获取
                result = await _dictionaryRepository.Entities
                .Where(r => ids.Contains(r.Id) && r.DeleteMark == null).ToListAsync();
            }      
            return result;
        }

        /// <summary>
        /// 处理字典值id拼接上所有父级id
        /// </summary>
        /// <param name="id">字典值id</param>
        /// <param name="organizeId">组织id</param>
        /// <returns></returns>
        [NonAction]
        public async Task<string> GetIdAsync(string id, string organizeId)
        {
            if (id.Contains(",")) return id;
            //验证是否为通用表单id
            var entity = await _dictionaryMongoDbRepository.GetAsync(_collection, m => m.Id == id && m.DeleteMark==null);
            if (entity == null) await _dictionaryRepository.FirstOrDefaultAsync(r=>r.Id==id && r.DeleteMark==null);
            if (entity == null) return id;
            //先从mongodb中获取
            var data = await _dictionaryMongoDbRepository.GetListAsync(_collection, m =>m.OrganizeId== organizeId && m.DeleteMark == null);
            if (!data.Any())
            {
                //再从sqlserver中获取
                data = await _dictionaryRepository.Entities.Where(r => r.OrganizeId == organizeId && r.DeleteMark == null).ToListAsync();
            }
            if (!data.Any()) return id;
            //获取所有上级包括自己
            data = data.TreeWhere(r => r.Id == id, r => r.Id, r => r.ParentId);
            //顺序反转
            data.Reverse();
            var ids = data.Select(r => r.Id).ToList();
            id = string.Join(',', ids);
            return id;
        }

        /// <summary>
        /// 处理字典值id拼接上所有父级id
        /// </summary>
        /// <param name="id">字典值id</param>
        /// <param name="data">字典值数据</param>
        /// <returns></returns>
        [NonAction]
        public string GetId(string id, List<DictionaryEntity> data)
        {
            if (id.Contains(",") || (data==null || data.Count<=0)) return id;
            //验证是否为通用表单id
            var entity = data.FirstOrDefault(r=>r.Id==id);
            if (entity == null) return id;
            //判断是否顶级
            if (string.IsNullOrWhiteSpace(entity.ParentId)) return id;
            //获取所有上级包括自己
            data = data.TreeWhere(r => r.Id == id, r => r.Id, r => r.ParentId);
            //顺序反转
            data.Reverse();
            var ids = data.Select(r => r.Id).ToList();
            id = string.Join(',', ids);
            return id;
        }

    }
}
